package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"

	"../helper/dao"
	"../helper/goconfig"
	"../helper/redisclient"
	"../helper/response"
)

var ch1 = make(chan string, 100)
var rc *redisclient.RedisClient
var dc *dao.DaoClient
var firstCh = make(chan string, 100)

var logFile *os.File
var debugLog *log.Logger

//
// 错误
const (
	NO_ERR    = 0 // 无错误
	ERR_OTHER = 1 // 参数错误或者其他
)

//
// 秒杀状态
const (
	SECKLLING_PENDING = 0 // 未开始
	SECKLLING_SUCESS  = 1 // 秒杀成功
	SECKLLING_FAIL    = 2 // 秒杀失败
	SECKLLING_REPEAT  = 3 // 重复提交
)

func seckill(w http.ResponseWriter, r *http.Request) {
	dealSeckill(w, r)
}

func userSeckill(w http.ResponseWriter, r *http.Request) {
	dealUserSeckill(w, r)
}

func productSeckill(w http.ResponseWriter, r *http.Request) {
	dealProductSeckill(w, r)
}

func isValidProductId(pid string) bool {
	return pid == "111" || pid == "222" || pid == "333"
}

func isValidUserId(uid string) bool {
	nid, err := strconv.Atoi(uid)
	return err == nil && nid >= 0
}

func dealSeckill(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	uid := r.FormValue("userid")
	pid := r.FormValue("productid")
	firstCh <- uid
	// ip过滤,关闭
	// ipvalid := ipFilter(uid, r)

	debugLog.SetPrefix("[Debug]")
  debugLog.Printf("uid: %s pid: %s\n", uid, pid)
	if !isValidUserId(uid) || !isValidProductId(pid) {
		responseStr, _ := response.SecKill(ERR_OTHER, SECKLLING_FAIL, "参数错误。")
		fmt.Fprintf(w, responseStr)
	} else {
		checkUser(w, uid)
	}
}

// 过滤ip，如果与第一次记录的ip不相同返回false，最终返回的errno自定义
func ipFilter(userid string, r *http.Request) bool {
	iparray := strings.Split(r.RemoteAddr, ":")
	if len(iparray) < 1 {
		return true
	}
	ip := iparray[0]
	fmt.Println(ip)
	hasIp, _ := rc.HGet("userip", userid)
	if hasIp == "" {
		rc.HSet("userip", userid, ip)
		return true
	} else if ip == hasIp {
		return true
	}

	return false
}

func dealUserSeckill(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	uid := r.FormValue("userid")
	pid := r.FormValue("productid")
	if !isValidUserId(uid) || !isValidProductId(pid) {
		responseStr, _ := response.UserSecKillInfoResponse(ERR_OTHER, SECKLLING_FAIL, -1)
		fmt.Fprintf(w, responseStr)
	} else {
		goodsid := queryUserKill(uid, pid)
		if goodsid == "" {
			responseStr, _ := response.UserSecKillInfoResponse(NO_ERR, SECKLLING_FAIL, -1)
			fmt.Fprintf(w, responseStr)
		} else {
			goodsidInt, _ := strconv.Atoi(goodsid)
			responseStr, _ := response.UserSecKillInfoResponse(NO_ERR, SECKLLING_SUCESS, goodsidInt)
			fmt.Fprintf(w, responseStr)
		}
	}
}

func dealProductSeckill(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	pid := r.FormValue("productid")
	if !isValidProductId(pid) {
		responseStr, _ := response.ProductSecKillInfoResponse(ERR_OTHER, nil)
		fmt.Fprintf(w, responseStr)
	} else {
		queryProductKill(w, pid)
	}
}

func queryProductKill(w http.ResponseWriter, pid string) {
	b := bytes.Buffer{}
	b.WriteString(pid)
	b.WriteString("_result")
	key := b.String()
	value, err := rc.HGetall(key)
	if err != nil {
		responseStr, _ := response.ProductSecKillInfoResponse(ERR_OTHER, nil)
		fmt.Fprintf(w, responseStr)
	} else {
		responseStr, _ := response.ProductSecKillInfoResponse(NO_ERR, value)
		fmt.Fprintf(w, responseStr)
	}
}

func queryUserKill(uid, pid string) string {
	b := bytes.Buffer{}
	b.WriteString(pid)
	b.WriteString("_result")
	key := b.String()
	value, err := rc.HGet(key, uid)
	if err == nil {
		return value
	} else {
		return ""
	}
}

func buyGoods() {
	 // debugLog.SetPrefix("[Debug]")
	// debugLog.Println(string(<-ch1), "读取通道")
}

func checkUser(w http.ResponseWriter, uid string) {
	uid = <-firstCh
	value := "0"
	value, err := rc.Get("product_111")
	if err != nil {
		value = "0"
	}
	if value == "0" {
		responseStr, _ := response.SecKill(NO_ERR, SECKLLING_FAIL, "秒杀已结束。")
		fmt.Fprintf(w, responseStr)
		//fmt.Fprintf(w, "秒杀已结束。")
		return
	}
	err = rc.Setnx(uid, "1")
	if err != nil {
		// debugLog.SetPrefix("[Debug]")
		// debugLog.Printf("接收到数据>%s \n", uid)

		responseStr, _ := response.SecKill(NO_ERR, SECKLLING_REPEAT, "已参与秒杀,请勿重复。")
		fmt.Fprintf(w, responseStr)

		//fmt.Fprintf(w, "已参与秒杀，请稍后查询秒杀结果。")
	} else {
		// debugLog.SetPrefix("[Debug]")
		// debugLog.Printf("接收到数据>%s \n", uid)

		ch1 <- uid

		responseStr, _ := response.SecKill(NO_ERR, SECKLLING_SUCESS, "已成功参与秒杀，请稍后查询秒杀结果。")
		fmt.Fprintf(w, responseStr)

		//fmt.Fprintf(w, "已成功参与秒杀，请稍后查询秒杀结果。")
	}
}

func resultDeal(tempUserId string, cnt int) {
    value := strconv.Itoa(cnt)
    userId, _ := strconv.Atoi(tempUserId)
    rc.HSet("111_result", tempUserId, value)
    dc.Insertorder(userId, 111, cnt, 1)
}

func reads() {
	for {
		tempUserId := string(<-ch1)
		value, err := rc.Get("product_111")
		cnt := 0
		if err == nil {
			cnt, _ = strconv.Atoi(value)
		}
		if cnt > 0 {
			go resultDeal(tempUserId, cnt)
			cnt = cnt - 1
			value := strconv.Itoa(cnt)
			rc.Set("product_111", value)
		}
	}
}

func pageNotFound(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "page not found")
}

func main() {
	// 读取配置文件并设置
	conf, err1 := goconfig.LoadConfigFile("../../conf/config.ini")
	if err1 != nil {
		fmt.Println("日志读取出错", err1)
		return
	}
	redishost, _ := conf.GetValue("redis", "redisAddr")
	redisport, _ := conf.GetValue("redis", "redisPort")
	mysqlhost, _ := conf.GetValue("mysql", "mysqlAddr")
	mysqlport, _ := conf.GetValue("mysql", "mysqlPort")
	rc = redisclient.NewRedisClient(map[string]string{"host": redishost + ":" + redisport}, 1000, 1000, 1000, 256, 256, 86400)
	dc = dao.NewDaoClient(mysqlhost+":"+mysqlport, 256, 256)

	logFolder, _ := conf.GetValue("log", "logPath")
	logFileName, _ := conf.GetValue("log", "logFileName")
	logFile, _ = os.Create(logFolder + logFileName)
	debugLog = log.New(logFile, "[Debug]", log.Llongfile)


	productconf, err2 := goconfig.LoadConfigFile("../../conf/product.ini")
	if err2 != nil {
		fmt.Println("日志读取出错", err2)
		return
	}
	product_111, _ := productconf.GetValue("product", "111")
	rc.Set("product_111", product_111)

	debugLog.SetFlags(log.LstdFlags)

	// 创建log日志文件
	go reads()

	runtime.GOMAXPROCS(runtime.NumCPU())
	http.HandleFunc("/", pageNotFound)
	http.HandleFunc("/shark/seckilling", seckill)
	http.HandleFunc("/shark/queryUserSeckillingInfo", userSeckill)
	http.HandleFunc("/shark/queryProductSeckillingInfo", productSeckill)
	err := http.ListenAndServe(":8887", nil) //创建一个http的路由，url是根路径，并监听8888端口

	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
